# -*- coding: utf-8 -*-
import os, sys, re, pprint, nltk, codecs, string
from nltk.corpus import toolbox
from config import *

class LexConverter:
    """An abstract superclass for readers for all lexicon formats. The translation
    function is different for each source format and to be specified."""

    def convert2TB(self, source, target):
        """Takes a dictionary in a given format and converts it to Toolbox
        @param source: source file
        @param target: target file
        """
        self.saveAsTB(self.translate(self.openUnicode(source)), target) 

    def openUnicode(self, file):
        """Opens a file in unicode encoding.
        @param file: file path.
        @return: file contents in unicode encoding.
        """
        return unicode(open(file,'r').read(), ENCODING)  
    
    def translate(self):
        "Placeholder for translation function, differs per source format."     
        raise NotImplementedError("Translation function missing...")
      
    def saveAsTB(self, dictionary, file):
        """Takes a dictionary that has been prepared for storage in Toolbox format and 
        creates new/overwrite TB file 'file' to put lexicon there
        @param dictionary: dictionary of the form {(VERB, SCF): [set of features], ...}
        @param file: file to be saved
        """
        file = codecs.open(file, "w", ENCODING)
        for i in dictionary:
            file.write("\n".join(i)+"\n"+"\n".join(dictionary[i])+"\n\n")
        file.close

class SynlexConverter (LexConverter):
    "Converter for Synlex."   
    
    def translate(self, lexicon):
        """Prepares the contents of a raw synlex lexicon to be saved in Toolbox format
        @param lexicon: raw lexicon in synlex format. 
        @return: a dictionary that can be saved to TB format
        """
        # split verbs
        split = lexicon.split("\n")
        # extract information and prepare for toolbox, dictionary: {(VERB, SCF): [set of features], ...}
        result = dict([])
        for i in split:
            if i != '':
                basic = re.search(u'(\w*); Id=(\d*); .*; \[(.*)\]',i)
                lexeme = '\lx ' + string.lower(basic.group(1))
                subcat = '\scf ' + self.genSCF(basic.group(3))
                source = '\cf synlex('+ basic.group(2)+')'
                glossF = set(['\gf ' + i for i in re.findall(u'Ex="([^"]*)"',i)])
                macros = set(['\\nt macro:' + i for i in re.findall(u'Macro=([^:]*):',i)])
                associes = set([u'\\nt associé:' + i for i in re.findall(u'Associé=([^:]*):',i)])
                result[(lexeme, subcat)] = result.get((lexeme, subcat), set([])).union(glossF).union(macros).union(associes).union(set([source]))
        return result
    
    def genSCF(self, sourceSCF):
        """Convert scf to generic scf format.
        We use the format SYNT:POS, SYNT:POS where for each argument SYNT is the syntactical category
        and POS the POS tag.
        @param sourceSCF: the raw data from the synlex lexicon
        @return: generic scf format
        """
        args = re.compile('a\d+:').split(sourceSCF)[1:]
        return ','.join([self.compileArg(a) for a in args])
    
    def compileArg(self, arg):
        values = re.search('(.*):.*:(.*):.*:.*:',arg)
        return self.formatArg(values.group(1))+':'+self.formatArg(values.group(2))      
    
    def formatArg(self, input):
        POStranslation = {'SN':'NP', 
                          'SUJ':'SUJ', 
                          'OBJ':'OBJ',
                          'OBJA':'A-OBJ',
                          'OBJDE':'DE-OBJ',
                          'SCOMPL':'VN', 
                          'OBL':'P-OBJ',
                          'LOC':'P-OBJ',  # 
                          'SINF':'VPinf' 
                          }
        try:
             return POStranslation[string.upper(input)]
        except:
             return string.upper(input)     
    
class LexschemConverter (LexConverter):
    "Converter for LexSchem."

    def translate(self, lexicon):
        """Prepares the contents of a raw lexschem lexicon to be saved in Toolbox format
        @param lexicon: raw lexicon in lexschem format. 
        @return: a dictionary that can be saved to TB format with keys for (verb, scf).
        """
        # split verbs
        split = lexicon.replace('\t','').split('\n\n')
        # extract information, item: [NUM, SUBCAT, VERB, SCF, COUNT, RELFREQ, EXAMPLE].
        data = [re.findall(r':[A-Z]+:(.*)\n?', i) for i in split]
        # prepare for toolbox, dictionary: {(VERB, SCF): [set of features], ...}
        result = dict([])
        for i in data:
            verb = string.lower(re.search(r'\+(.*)', i[2]).group(1))
            refl = ''
            if verb[:3]=='se_': verb=verb[3:]; refl=',refl:CL'
            
            lexeme = '\lx ' + verb
            subcat = '\scf ' + self.genSCF(i[3], refl)
            glossF = set(['\gf ' + self.findExample(j) for j in i[6].split(';')])
            source = '\cf lexschem(%s)' % i[0]
            # add it to an existing key or create a new (empty) one if it doesn't exist
            result[(lexeme, subcat)] = result.get((lexeme, subcat), set([])).union(glossF).union(set([source]))
        return result
    
    def genSCF(self, sourceSCF, refl=''):
        """Convert scf to generic scf format.
        We use the format SYNT:POS, SYNT:POS where for each argument SYNT is the syntactical category
        and POS the POS tag.
        @param sourceSCF: the raw data from the synlex lexicon
        @return: generic scf format
        """
        if sourceSCF=='INTRANS': 
            return 'SUJ:?'+refl
        else:
            args = re.compile('_').split(sourceSCF)
            #subject has to be added, syntactical category unknown (not specified in lexschem format) 
            #reflexive verbs are stored as 'se_<verb>'       
            return 'SUJ:?,'+','.join([self.compileArg(a) for a in args])+refl
    
    def compileArg(self, arg):
        if re.compile('SN').match(arg): return 'OBJ:NP'
        if re.compile('SINF').match(arg): return 'OBJ:VPinf'
        if re.compile('SA').match(arg): return 'OBJ:AP'  
        if re.compile('COMPL').match(arg): return 'OBJ:Ssub' 
        if re.compile('SP\[de\+SN\]').match(arg): return 'DE-OBJ:NP'
        if re.compile('SP\[à\+SN\]').match(arg): return 'A-OBJ:NP'
        if re.compile('SP\[.*\+SN\]').match(arg): return 'P-OBJ:NP'
        if re.compile('SP\[de\+SINF\]').match(arg): return 'DE-OBJ:VPinf'
        if re.compile('SP\[à\+SINF\]').match(arg): return 'A-OBJ:VPinf'
        if re.compile('SP\[.*\+SINF\]').match(arg): return 'P-OBJ:VPinf'
        if re.compile('SP\[de\+VINF\]').match(arg): return 'DE-OBJ:VPinf'
        if re.compile('SP\[à\+VINF\]').match(arg): return 'A-OBJ:VPinf'
        if re.compile('SP\[.*\+VINF\]').match(arg): return 'P-OBJ:VPinf'       
        return string.upper(arg)
    
    def findExample(self, id):
        """Find the real example in the Le Monde corpus (in lexschem only a reference to 
        the examples is included). 
        @param id: reference to the example
        @return: the real example
        
        @todo: find the thing!        
        """
        return 'lemonde: %s' % id
        
class TreelexConverter (LexConverter):
    "Converter for Treelex."

    def translate(self, lexicon):
        """Prepares the contents of a raw treelex lexicon to be saved in Toolbox format
        @param lexicon: raw lexicon in treelex format. 
        @return: a dictionary that can be saved to TB format with keys for (verb, scf).
        """
        # split verbs
        split = [i.split('\n') for i in lexicon.replace(' ', '').split('\n\n===')][1:]  
        # extract information
        data = [(re.search(r'(.*)\(', i[0]).group(1),re.findall(r'([A-Za-z\,:-]*)\(\d*\)', ';'.join(i[1:]))) for i in split]
        # prepare for toolbox, dictionary: {(VERB, SCF): [set of features], ...}
        result = dict([])
        for i in data: 
            lexeme = '\lx %s' % string.lower(i[0])
            subcats= set([('\scf '+self.genSCF(j)) for j in i[1]])
            source = '\cf treelex'
            # add it to an existing key or create a new (empty) one if it doesn't exist
            for subcat in subcats:
                result[(lexeme, subcat)] = result.get((lexeme, subcat), set([])).union(set([source]))
        return result
    
    def genSCF(self, sourceSCF):       
        """Convert scf to generic scf format. 
        We use the format SYNT:POS, SYNT:POS where for each argument SYNT is the syntactical category
        and POS the POS tag.
        This format is also used in the treelex lexicon, so here no translation is required.
        @param sourceSCF: the raw data from the synlex lexicon
        @return: generic scf format
        """
        return sourceSCF.replace(' ', '')